package com.rtf.framework.web.mvc.rest;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.rtf.framework.common.config.AppProperties;
import com.rtf.framework.common.support.AppRuntimeDict;
import com.rtf.framework.common.support.anno.AppDict;
import com.rtf.framework.common.util.*;
import com.rtf.framework.web.http.AppIBaseController;
import com.rtf.framework.web.http.ResponseParam;
import com.rtf.framework.web.mvc.rest.curd.AppFunAdaptorController;
import com.rtf.framework.web.mvc.rest.curd.AppFunQueryController;
import com.rtf.framework.web.mvc.rest.curd.AppTreeConfig;
import com.rtf.framework.web.mvc.rest.dict.SysDict;
import com.rtf.framework.web.mvc.rest.dict.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 运行时字典项
 * @Author : liupeng
 * @Date : 2018/7/25 17:11
 * @Modified By
 */
@Slf4j
@ResponseBody
@RequestMapping( value = {"/api"+AppDictionaryRuntimeController.BASE_PATH , "/api/auth/dictionary/runtime" } )
public class AppDictionaryRuntimeController extends AppFunAdaptorController {

    public static final String BASE_PATH  = "/dictionary/runtime" ;

    // 扫描 AppRuntimeDict 的基础包
    private static Set<String> dictRuntimeClassPackages = Sets.newHashSet() ;

    private static Map<String,Class<? extends AppRuntimeDict>> runtimeDictMap = Maps.newHashMap() ;

    @Autowired
    private SysDictService sysDictService ;

    /**
     * 添加运行时字典项的基类
     * @param dictRuntimeClassPackage
     */
    public static void addDictRuntimeClassPackage(String dictRuntimeClassPackage){
        if( dictRuntimeClassPackages.contains( dictRuntimeClassPackage ) ){
            return ;
        }
        Reflections reflections = new Reflections( dictRuntimeClassPackage ) ;

        Set<Class<? extends AppRuntimeDict>> appRuntimeDictClasses = reflections.getSubTypesOf( AppRuntimeDict.class ) ;

        List<String> appDictNames = Lists.newArrayList() ;

        for (Class<? extends AppRuntimeDict> appRuntimeDictClass : appRuntimeDictClasses) {
            AppDict appDict = UtilAnnotation.findAnnotation( appRuntimeDictClass , AppDict.class ) ;

            String dictParentCode = appDict!=null ? appDict.value() : null ;

            if(UtilString.isBlank( dictParentCode )){
                log.debug("运行时字典 {} 的rest地址访问为空" , appRuntimeDictClass.getName());
                continue;
            }
            // 记录所有的字典名称
            appDictNames.add( appDict.value() ) ;

            runtimeDictMap.put( dictParentCode , appRuntimeDictClass ) ;
        }

        log.debug("运行时字典访问规则：/dictionary/runtime/[名称] , 系统中字典名称 {} , " , Joiner.on(" , ").join(appDictNames) ) ;
    }

    public AppDictionaryRuntimeController(){
        // 添加运行时字典映射
        for (String basePackage : AppProperties.AppPackage.BASE_PACKAGES) {
            addDictRuntimeClassPackage( basePackage ) ;
        }
    }

    @RequestMapping("{code}")
    public Object runtimeDictionary(@PathVariable String code ,
                                           @RequestParam(required = false) String excludes ,
                                           @RequestParam(required = false , defaultValue = "true") Boolean wrapped){
        if( !runtimeDictMap.containsKey( code ) ){
            return ResponseParam.fail().message("没有指定的字典项") ;
        }

        Class<?> runtimeDictClass = runtimeDictMap.get( code ) ;

        if( !runtimeDictClass.isEnum() ){
            return ResponseParam.fail().message("运行时字典必须为枚举类型") ;
        }

        AppRuntimeDict[] appRuntimeDicts = null ;

        // 通过反射机制获取所有的枚举类型
        try {
            Method valuesMethod = runtimeDictClass.getMethod("values") ;
            appRuntimeDicts = (AppRuntimeDict[])valuesMethod.invoke( null , null ) ;
        } catch (Exception e) {
            return ResponseParam.fail().message("运行时字典必须为枚举类型, " + e.getMessage()) ;
        }

        ResponseParam responseParam = ResponseParam.success() ;

        // 构造字典列表
        if( appRuntimeDicts != null ){
            // 获取排除的编码
            List<String> excludeCodes = UtilString.splitByComma( excludes ) ;

            List<Map<String,String>> dictList = Lists.newArrayListWithExpectedSize(appRuntimeDicts.length ) ;
            Arrays.stream(appRuntimeDicts)
                    .filter( appRuntimeDict -> !excludeCodes.contains( appRuntimeDict.name() ) )
                    .forEach( appRuntimeDict -> {
              Map<String,String> item = Maps.newHashMapWithExpectedSize(2) ;
              item.put("code" , appRuntimeDict.name() ) ;
              item.put("name" , appRuntimeDict.getRemark() ) ;
              dictList.add( item ) ;
            }) ;

            // 如果不需要进一步封装结构，直接返回list
            if( !wrapped ){
                return dictList ;
            }

            responseParam.datalist( dictList ) ;
        }

        return responseParam ;
    }

    /**
     * 获取运行时字典实例
     *
     * @param code
     * @return
     */
    private AppRuntimeDict[] getRuntimeDictionary(String code) {
        Class<?> runtimeDictClass = runtimeDictMap.get(code);
        AppRuntimeDict[] appRuntimeDicts = null;
        try {
            Method valuesMethod = runtimeDictClass.getMethod("values");
            appRuntimeDicts = (AppRuntimeDict[]) valuesMethod.invoke(null, null);
        } catch (Exception e) {
        }
        return appRuntimeDicts == null ? new AppRuntimeDict[]{} : appRuntimeDicts;
    }

    /**
     * 应用分类查询全部
     *
     * @return
     * @throws Exception
     */
    @PostMapping("/bycode/{code}")
    public ResponseParam querySubDict(@PathVariable("code") String code, @RequestBody Map<String, Object> params) {
        // 获取需要排序的id
        JSONArray excludeIdJsonArray = UtilMap.isNotEmpty(params) ? new JSONObject(params).getJSONArray("excludes") : null;

        List<String> excludeIds = UtilCollection.isNotEmpty(excludeIdJsonArray) ? Lists.newArrayList(excludeIdJsonArray.toArray(new String[]{})) : Lists.newArrayList();
        //查询字典列表
        List<Map<String, Object>> targetDtoList = Lists.newArrayList() ;
        if (runtimeDictMap.containsKey(code)) {
            // 查询运行时字典项
            AppRuntimeDict[] appRuntimeDicts = getRuntimeDictionary(code);
            for (AppRuntimeDict appRuntimeDict : appRuntimeDicts) {
                if (excludeIds.contains(appRuntimeDict.name())) {
                    continue;
                }
                Map<String, Object> item = Maps.newHashMapWithExpectedSize(2);
                item.put("id", appRuntimeDict.name());
                item.put("name", appRuntimeDict.getRemark());
                targetDtoList.add(item);
            }
        } else {
            // 查询数据库字典项
            List<SysDict> subDictList = sysDictService.queryChildDict(code,UtilMap.getBoolean(params,"allChild",false));
            for (SysDict appDict : subDictList) {
                if (excludeIds.contains(appDict.getCode())|| UtilString.equals(code,appDict.getCode())) {
                    continue;
                }
                Map<String, Object> item = Maps.newHashMapWithExpectedSize(2);
                item.put("id", appDict.getCode());
                item.put("code", appDict.getId());
                item.put("parentId", appDict.getParentId());
                item.put("name", appDict.getName());
                item.put("title", appDict.getName());
                item.put("thumbnail",appDict.getThumbnail());
                item.put("url",appDict.getPath());
                targetDtoList.add(item);
            }
            //4.1 对列表进行返回前的最后处理
            targetDtoList = queryListPostHandle(targetDtoList);
        }

        return ResponseParam.success().datalist(targetDtoList);
    }

    /**
     * 列表查询的后续处理，只针对列表进行处理
     *
     * @param targetDtoList
     * @return
     */
    @Override
    public List<Map<String, Object>> queryListPostHandle(List targetDtoList) {
        // 对返回的结果是否转换为树的处理
        AppTreeConfig treeConfig = queryListTreeConfig();
        treeConfig.setIdField("code");
        treeConfig.setEnable(true);
        if (treeConfig != null && treeConfig.isEnable()) {
            targetDtoList = UtilTree.getTreeData(targetDtoList, treeConfig.getIdField(), treeConfig.getParentIdField(),
                    treeConfig.getChildrenKey(), treeConfig.isRemoveIdField());
        }

        return targetDtoList;
    }
}
